import re
import sys
from threading import Thread, Condition, Event, Lock
from getpass import getpass
from support import Singleton

_io_lock = Lock()

def get_plaintext_input(self, prompt):
    _io_lock.acquire()
    try:
        return raw_input(prompt)
    finally:
        _io_lock.release()

def get_password_input(self, prompt):
    _io_lock.acquire()
    try:
        return getpass(prompt)
    finally:
        _io_lock.release()

def write_stdout(self, message):
    _io_lock.acquire()
    try:
        sys.stdout.write(message)
    finally:
        _io_lock.release()

def write_stderr(self, message):
    _io_lock.acquire()
    try:
        sys.stderr.write(message)
    finally:
        _io_lock.release()
        

class Responder(object):
    
    def __init__(self, expecter, callback, triggers):
        self.__expecter = expecter
        self.__callback = callback
        self.__triggers = triggers
    
    def __call__(self, buffer_str, stdin):
        did_respond = False
        
        if self.__triggers > 0:
            did_respond = self.__callback(buffer_str, stdin)
            self.__triggers -= 1
        
        if self.__triggers <= 0:
            self.__expecter.remove(self)
        
        return did_respond


class Expecter(object):
    
    def __init__(self):
        self.__responders = set()
        self.__cv = Condition()
    
    def handle(self, buffer_str, stdin):
        responders_to_dispatch = []
        self.__cv.acquire()
        try:
            for responder in self.__responders:
                responders_to_dispatch.append( responder )
        finally:
            self.__cv.release()
        for responder in responders_to_dispatch:
            responder(buffer_str, stdin)
        
    def remove(self, responder):
        self.__cv.acquire()
        try:
            self.__responders.remove(responder)
            self.__cv.notifyAll()
        finally:
            self.__cv.release()
        
    def _append_responder(self, responder):
        self.__cv.acquire()
        try:
            self.__responders.append(responder)
            self.__cv.notifyAll()
        finally:
            self.__cv.release()
    
    def expect_input_for(self, pattern, triggers = 1):
        re_pattern = re.compile(pattern)
        def respond(self, buffer_str, stdin):
            if re_pattern.search(buffer_str):
                input_str = get_plaintext_input(prompt = buffer_str)
                stdin.write(input_str)
        responder = Responder(self, respond, triggers)
        self._append_responder( responder )
    
    def expect_password_for(self, pattern, triggers = 1):
        re_pattern = re.compile(pattern)
        def respond(self, buffer_str, stdin):
            if re_pattern.search(buffer_str):
                input_str = get_password_input(prompt = buffer_str)
                stdin.write(input_str)
        responder = Responder(self, respond, triggers)
        self._append_responder( responder )
    
    def respond_with_input_for(self, pattern, plaintext, triggers = 1):
        re_pattern = re.compile(pattern)
        def respond(self, buffer_str, stdin):
            if re_pattern.search(buffer_str):
                stdin.write(plaintext)
        responder = Responder(self, respond, triggers)
        self._append_responder( responder )
    
    def respond_with_password_for(self, pattern, password, triggers = 1):
        re_pattern = re.compile(pattern)
        def respond(self, buffer_str, stdin):
            if re_pattern.search(buffer_str):
                stdin.write(password)
        responder = Responder(self, respond, triggers)
        self._append_responder( responder )
    